/*
 * RouteScannerFW.java
 *
 * Created on April 9, 2007, 8:19 PM
 *
 * Copyright 2008 David D. Emory
 * 
 * This file is part of Five Points. See <http://www.fpdev.org> for
 * additional information regarding the project.
 * 
 * Five Points is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Five Points is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Five Points.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.fpdev.core.algo.path;

import org.fpdev.core.transit.SubRoute;
import org.fpdev.core.transit.Timetable;
import org.fpdev.core.routenet.RouteNetwork;
import org.fpdev.core.routenet.RNode;
import org.fpdev.core.routenet.RLink;
import org.fpdev.core.transit.*;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import org.fpdev.util.FPUtil;

/**
 * The subclass of RouteScanner for searching "forwards" through the 
 * RouteNetwork (i.e. "depart-at" searches).
 * 
 * @author demory
 */
public class RouteScannerFW extends RouteScanner {

  /** Creates a new instance of RouteScannerFW */
  public RouteScannerFW(RouteNetwork rteNet, Set<String> validSubs, RNode origin, RNode dest, int time, CalendarService service, double walkSpeedFPS) {
    super(rteNet, validSubs, origin, dest, time, service, walkSpeedFPS);
  }

  protected int getFinalLinkType() {
    return RLink.RLINK_ALIGHT;
  }

  protected Iterator<RLink> getPotentialFinalLinks() {
    return rteNet_.inLinks(dest_);
  }

  protected RNode startScanNode() {
    return origin_;
  }

  protected RNode nodeEncounteredFirst(RLink link) {
    return rteNet_.fromNode(link);
  }

  protected int getType() {
    return FW;
  }

  protected void addToSet(RNode rNode, int time) {


    Iterator<RLink> links = rteNet_.outLinks(rNode);
    boolean dbg = false;// curLimit_ == 2 && rNode.getID().equals("s_N5");
    if (dbg) {
      System.out.println("Processing rNode: " + rNode.getID() + " time=" + FPUtil.sTimeToStr(time));
      //for(int l = curLimit_-1; l>=1; l--) {
      int l = curLimit_ - 1;
      Iterator<RLink> pLinks = parents_[l].get(rNode).iterator();
      String plStr = "";
      while (pLinks.hasNext()) {
        plStr += pLinks.next().getID() + (pLinks.hasNext() ? ", " : "");
      }
      System.out.println(" path to: " + plStr);
    //}

    }
    /*if(curLimit_ == 2) {
    System.out.println("Processing rNode: "+rNode.getID()+" time="+CUtil.sTimeToStr(time));
    Iterator<CRouteLink> pLinks = parents_[curLimit_-1].get(rNode).iterator();
    String plstr = "";
    while(pLinks.hasNext()) {
    plstr += pLinks.next().getID();
    }
    System.out.println("parent links: "+plstr+",");
    }*/

    if (finalLinks_.containsKey(rNode)) {
      Iterator<RLink> flinks = finalLinks_.get(rNode).iterator();
      while (flinks.hasNext()) {
        RLink fLink = flinks.next();
        if (dbg) {
          System.out.println(" D flink: " + fLink.getID());
        }
        foundPotential(new LinkedList<RLink>(), fLink);
      }
    }

    while (links.hasNext()) {
      RLink link = links.next();
      SubRoute sub = link.getSubRoute();
      if (!validSubs_.contains(sub.getMasterID())) {
        continue;
      }

      int timeOnSub = (curLimit_ > 1) ? 60 : 0; //0; //60;
      if (dbg) {
        System.out.println(" Processing rlink: " + link.getID());
      }
      //System.out.println(" Processing sub: "+sub.getFullID());
      int rli = sub.getRouteLinkIndex(link.getStartDLI());
      List<RLink> parents = new LinkedList<RLink>();
      parents.add(link); // add first link, possibly a board link
      for (int i = rli; i < sub.routeLinkCount(); i++) {
        RLink subLink = sub.getRouteLink(i);
        if (i > rli) {
          parents.add(subLink);
        }
        timeOnSub += (i > rli) ? subLink.timeCostFW(time + timeOnSub, service_, walkSpeedFPS_, 0) : link.timeCostFW(time + timeOnSub, service_, walkSpeedFPS_, 0);
        if (timeOnSub < 0 || timeOnSub > 500000) {
          break;
        }
        RNode node = rteNet_.toNode(subLink);
        if (dbg) {
          System.out.println("  subLink: " + subLink.getID() + " to " + node.getID() + ", timeOnSub: " + FPUtil.elapsedTimeStr(timeOnSub));
        }
        //if(curLimit_==2 && node.getID().equals("s_5P")) System.out.println("considering time "+(time+timeOnSub));

        if (reachableSets_[curLimit_].contains(node) && (time + timeOnSub) < nodeTimes_[curLimit_].get(node).intValue()) {
          reachableSets_[curLimit_].remove(node);
        }
        if (reachableSets_[curLimit_].add(node)) {
          List<RLink> pCopy = new LinkedList<RLink>();
          pCopy.addAll(parents);
          /*if(curLimit_<=3 && node.getID().equals("s_N5")) {
          String pls = "";
          Iterator<CRouteLink> iter = pCopy.iterator();
          while(iter.hasNext()) pls += iter.next().getID()+",";
          System.out.println("   set N5, time: "+CUtil.sTimeToStr(time+timeOnSub));
          System.out.println("   time="+time);
          System.out.println("   timeOnSub="+timeOnSub);
          System.out.println("   links: "+pls);
          }*/
          try {
            nodeTimes_[curLimit_].put(node, new Integer(time + timeOnSub));
            parents_[curLimit_].put(node, pCopy);
          } catch (Exception e) {
            e.printStackTrace();
            return;
          }
          if (node == dest_) {
            foundPotential(parents, null);
          }
        }
        if (finalLinks_.containsKey(node)) {
          Iterator<RLink> flinks = finalLinks_.get(node).iterator();
          while (flinks.hasNext()) {
            RLink fLink = flinks.next();
            if (dbg) {
              System.out.println("   flink: " + fLink.getID());
            }
            if (fLink.getSubRoute().getFullID().equals(sub.getFullID())) {
              foundPotential(parents, fLink);
            }
          }
        }
      }
    }
  }

  public void foundPotential(List<RLink> parents, RLink lastLink) {
    try {
      List<RLink> pathLinks = new LinkedList<RLink>();

      Iterator<RLink> pIter = parents.iterator();
      while (pIter.hasNext()) {
        pathLinks.add(pIter.next());
      }
      if (lastLink != null) {
        pathLinks.add(lastLink);
      }
      for (int l = curLimit_ - 1; l >= 1; l--) {
        List<RLink> pLinks = parents_[l].get(rteNet_.fromNode(pathLinks.get(0)));
        if (pLinks != null) {
          ListIterator<RLink> plIter = pLinks.listIterator(pLinks.size());
          while (plIter.hasPrevious()) {
            pathLinks.add(0, plIter.previous());
          }
        }
      }
      Iterator<RLink> links = pathLinks.iterator();
      String lstr = "";
      while (links.hasNext()) {
        lstr += links.next().getID() + ",";
      }
      potentials_.add(pathLinks);
    /*if(curLimit_ == 3) {
    System.out.println("   ADDED potential "+(potentials_.size()-1));
    System.out.println("   links: "+lstr);
    }*/
    } catch (Exception e) {
      e.printStackTrace();
      return;
    }
  }
}
